package com.firemoss.atonement.frameworks.magicbus
{
	import com.firemoss.atonement.mapper.Mapper;
	import com.firemoss.magicbus.dto.DTOAssembler;
	
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	
	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	
	public class MappingAssembler implements DTOAssembler
	{
		public var mapper : Mapper
		public var entityClass : Class
		public var dtoClass : Class
		public var defaultCollectionClass : Class = ArrayCollection
		public var ignoredDisassemblyClasses : Array = [ Boolean, ByteArray, Number, String ]
			
		public function MappingAssembler( mapper : Mapper = null, entityClass : Class = null, dtoClass : Class = null )
		{
			this.mapper = mapper
			this.entityClass = entityClass
			this.dtoClass = dtoClass
		}
		
		public function assemble(source:*, target:*=null):Object
		{
			if ( source is Array )
			{
				return assembleArray( source as Array, target )
			}
			else if ( source is IList )
			{
				return assembleCollection( source as IList, target )
			}
			else
			{
				return mapper.map(
					source,
					target != null ? target : new entityClass()
				)
			}
		}
		
		public function disassemble(source:Object, target:*=null):*
		{
			if ( disassemblyShouldIgnore( source ) )
			{
				return source
			}
			if ( source is Array )
			{
				return disassembleArray( source as Array, target )
			}
			else if ( source is IList )
			{
				return disassembleCollection( source as IList, target )
			}
			else
			{
				return mapper.map(
					source,
					target != null ? target : new dtoClass()
				)
			}
		}
		
		public function assembleArray(source:Array, target:Array=null):Array
		{
			return iterableDelegate( Array, assemble, source, target )
		}
		
		public function disassembleArray(source:Array, target:Array=null):Array
		{
			return iterableDelegate( Array, disassemble, source, target )
		}
		
		public function assembleCollection(source:IList, target:IList=null):IList
		{
			var res : * = iterableDelegate( defaultCollectionClass, assemble, source, target )
			
			return res;
		}
		
		public function disassembleCollection(source:IList, target:IList=null):IList
		{
			return iterableDelegate( defaultCollectionClass, disassemble, source, target )
		}
		
		public function iterableDelegate(targetClass : Class, targetFunction : Function, source:*, target:*=null):*
		{
			target = (target == null ) ? new targetClass() : target;
			
			for ( var i:int = 0; i<source.length; i++ )
			{
				var args : Array = [
					source[ i ],
					( i < target.length ) ? target[ i ] : null
				];
				
				var mapped : * = targetFunction.apply( this, args );
				
				if ( i <= target.length )
				{
					if ( target is IList )
					{
						target.addItem( mapped )
					}
					else
					{
						target[ i ] = mapped
					}
				}
				else
				{
					target[ i ] = mapped
				}
				/*
				target[ i ] = targetFunction.apply( this [
					source[ i ],
					( i < target.length ) ? target[ i ] : null
				] )
				*/
			}
			
			return target
		}
		
		
		private function disassemblyShouldIgnore( source : * ) : Boolean
		{
			for each ( var clazz : Class in ignoredDisassemblyClasses )
			{
				if ( source is clazz ) return true 
			}
			
			return false
		}
	}
}